package gameEngine;

import java.util.Observable;
import java.util.Date;


/**
 * Classe contenant toutes les données du jeu et qui se charge de les mettes à jour
 *
 */
public class GameData  extends Observable
{

	/**
	 * Taille du terrain
	 */
	private int fieldWidth;
	private int fieldHeight;
	
	/**
	 * Taille des équipes
	 */
	private int teamSize;
	
	/**
	 * Les entités
	 */
	private Player[] team1;
	private Player[] team2;
	private Ball ball;
	
	/**
	 * Pour le update, 'date' su dernier passage
	 */
	private long prevTime;
	
	/**
	 * Date, pour avoir les infos sur le temps
	 */
	private Date D;
	
	
	
	public GameData(int fieldWidth, int fieldHeight, int teamSize) 
	{
            this.fieldHeight = fieldHeight;
            this.fieldWidth = fieldWidth;
            this.teamSize = teamSize;
            
            ball = new Ball(((fieldWidth-Ball.SIZE)/2), ((fieldHeight-Ball.SIZE)/2));
            team1 = new Player[teamSize];
            team2 = new Player[teamSize];
            
            
            //Position initiale par défaut des joueur, sera changé selon les transmissions des IA (position initiale qu'ils)
            for(int i = 0; i < teamSize; i++) 
            {
                team1[i] = new Player((Player.SIZE/2), (i*fieldHeight/(teamSize-1)));
                team2[i] = new Player((fieldWidth-Player.SIZE/2), (i*fieldHeight/(teamSize-1)));
            }
            
            //Init gestion du temps
            D = new Date();
            prevTime = D.getTime();
	}
        
        
	/**
	 * Gére la collision d'un joueur avec une des équipes (groupe de Player)
	 * Normalement, vu que effVel et Integer sont des objet, ils sont modifié
	 */
	private void collision_with_team(Player P, Player[] team, Float effVelVal, Float effVelAng, Integer nbrCollide)
	{
		for(int j=0; j<teamSize; j++)
    	{
			//Verif reference ?
    		if(P.get_position().getX() == team[j].get_position().getX() && P.get_position().getY() == team[j].get_position().getY()) // le même
    		{}
    		else
    		{
    			//detection collision
    			if( P.get_position().distance_to( team[j].get_position() ) < 2 * Player.SIZE ) //Ah oui, on s'est rentré dedans
    			{
    				if(nbrCollide == 0) //premiere collision, on change les valuer de effVel
    				{
    					nbrCollide++;
    					//On prend l'angle de P2(j) à P1(i), d'où, ejection de P1
    					int y = ( team[j].get_position().getY() -  P.get_position().getY() );
    					int x = ( team[j].get_position().getX() -  P.get_position().getX() );
    					effVelAng = (float) Math.atan( y/x );
    					
    					//Distance entre les joueurs (/2 car les deux vont bouger)
    					effVelVal = (float) (P.get_position().distance_to( team[j].get_position() )/2); 
    				}
    				else //Collisions multiples, il faut modifier l'angle en conséquence ( et la valuer)
    				{
    					nbrCollide++;
    					
    					//On prend l'angle de P2(j) à P1(i), d'où, ejection de P1
    					int y = ( team[j].get_position().getY() -  P.get_position().getY() );
    					int x = ( team[j].get_position().getX() -  P.get_position().getX() );
    					float newEffVelAng = (float) Math.atan( y/x );
    					
    					//A verif
    					//Si écart > pi on passe en [0, 2PI], pour que la moy soit dans le bon sens (faire un dessin)
    					if( Math.abs( effVelAng - newEffVelAng ) > Math.PI )
    					{
    						if (effVelAng < 0) 
    						{ effVelAng = (float) (effVelAng + 2*Math.PI);  }
    						if (newEffVelAng < 0) 
    						{ newEffVelAng = (float) (newEffVelAng + 2*Math.PI);  }
    						
    						effVelAng = effVelAng + newEffVelAng / nbrCollide; // moyenne
    						
    						//repasse en [-pi, pi]
    						if (effVelAng > Math.PI) 
    						{ effVelAng = (float) (effVelAng - 2*Math.PI); }
    					}
    					else //sinon on reste en [-pi, pi]
    					{
    						effVelAng = effVelAng + newEffVelAng / nbrCollide;           						
    					}
    					
    					//Distance entre les joueurs (pas /2, pour test)
    					effVelVal = (float) P.get_position().distance_to( team[j].get_position() );             					
    				}
    				
    			}    			
    		}
    		
    	}
	}
	
	/**
	 * Méthode qui modifie les positions et les vitesses des entités en fonction des vitesses actuelles.
	 * Gére également les collisions.
	 */
	public void update() 
	{
            this.setChanged();
            this.notifyObservers();
            
            //Temps mis depuis la derniére fois
            long actTime = D.getTime(); // en milisec
            
            float sec = (float) ((actTime - prevTime) * 1000.0);
            
            
            //On va pas équipe après équipe, mais joueur après joueur, en alternant un équipe et l'autre
            
        	//1ere étape, détection des collisions
        	//On parcourt tout les autres joueurs, et on modifie la vitesse en conséquence
            //On sépare bien les deux étapes, pour éviter qu'un seul joueur ne soit affecté
            
            for(int i=0; i<teamSize; i++)
            {
            	//TEAM 1
            	
            	//Objet pour pouvoir être modifier dans méthodes
            	Float effVelVal = team1[i].get_velVal(); //effective Velocity Value, valeur réel de la vitesse
            	Float effVelAng = team1[i].get_velAngle(); // la même mais sur l'angle
            	Integer nbrCollide = 0; // nombre de collision detecté
            	
            	//Les autres joueurs team 1
            	collision_with_team(team1[i], team1, effVelVal, effVelAng, nbrCollide);            	
            	//Team2 maitenanant
            	collision_with_team(team1[i], team2, effVelVal, effVelAng, nbrCollide);
            	
            	            	          	
            	//S'il est dans un mur, priorité sur le mur (annule les autres pour le moment)
            	//Ou alors on gére pas ça ici
            	//Rappel: (0,0) = centre du terrain  
            	
            	if(Math.abs( team1[i].get_position().getX() ) + Player.SIZE > fieldWidth/2 || Math.abs( team1[i].get_position().getY() ) + Player.SIZE > fieldHeight/2)
            	{
	            	int XtoMove = 0;
	            	int YtoMove = 0;
            	
	            	//Si sa position en X + sa taille dépasse un demi terrain : on touche un bord
	            	if( Math.abs( team1[i].get_position().getX() ) + Player.SIZE > fieldWidth/2 )
	            	{ 
	            		int sign = (Math.abs( team1[i].get_position().getX() ) / team1[i].get_position().getX() );
	            		XtoMove =  sign * ( Math.abs(team1[i].get_position().getX())  + Player.SIZE - fieldWidth/2 ); //Signe, puis valuer
	            	} 
	            	if( Math.abs( team1[i].get_position().getY() ) + Player.SIZE > fieldHeight/2 )
	            	{ 
	            		int sign = (Math.abs( team1[i].get_position().getY() ) / team1[i].get_position().getY() );
	            		YtoMove =  sign * ( Math.abs(team1[i].get_position().getY())  + Player.SIZE - fieldHeight/2 ); //Signe, puis valuer
	            	} 
	            	
	            	//Polaire
	            	effVelAng = (float) Math.atan2(YtoMove, XtoMove);
	            	effVelVal = (float) Math.sqrt( Math.pow(YtoMove, 2) + Math.pow(XtoMove, 2) );
            	}
            	
            	//modif de la vitesse
            	team1[i].set_velAngle(effVelAng.floatValue());
            	team1[i].set_velVal(effVelVal.floatValue() / sec); // distance on time
            	
            	
            	//TEAM 2 (epic copy paste)
            	
            	//Objet pour pouvoir être modifier dans méthodes
            	 effVelVal = team2[i].get_velVal(); //effective Velocity Value, valeur réel de la vitesse
            	 effVelAng = team2[i].get_velAngle(); // la même mais sur l'angle
            	 nbrCollide = 0; // nombre de collision detecté
            	
            	//Les autres joueurs team 1
            	collision_with_team(team2[i], team1, effVelVal, effVelAng, nbrCollide);            	
            	//Team2 maitenanant
            	collision_with_team(team2[i], team2, effVelVal, effVelAng, nbrCollide);
            	
            	            	          	
            	//S'il est dans un mur, priorité sur le mur (annule les autres pour le moment)
            	//Ou alors on gére pas ça ici
            	//Rappel: (0,0) = centre du terrain  
            	
            	if(Math.abs( team2[i].get_position().getX() ) + Player.SIZE > fieldWidth/2 || Math.abs( team2[i].get_position().getY() ) + Player.SIZE > fieldHeight/2)
            	{
	            	int XtoMove = 0;
	            	int YtoMove = 0;
            	
	            	//Si sa position en X + sa taille dépasse un demi terrain : on touche un bord
	            	if( Math.abs( team2[i].get_position().getX() ) + Player.SIZE > fieldWidth/2 )
	            	{ 
	            		int sign = (Math.abs( team2[i].get_position().getX() ) / team2[i].get_position().getX() );
	            		XtoMove =  sign * ( Math.abs(team2[i].get_position().getX())  + Player.SIZE - fieldWidth/2 ); //Signe, puis valuer
	            	} 
	            	if( Math.abs( team2[i].get_position().getY() ) + Player.SIZE > fieldHeight/2 )
	            	{ 
	            		int sign = (Math.abs( team2[i].get_position().getY() ) / team2[i].get_position().getY() );
	            		YtoMove =  sign * ( Math.abs(team2[i].get_position().getY())  + Player.SIZE - fieldHeight/2 ); //Signe, puis valuer
	            	} 
	            	
	            	//Polaire
	            	effVelAng = (float) Math.atan2(YtoMove, XtoMove);
	            	effVelVal = (float) Math.sqrt( Math.pow(YtoMove, 2) + Math.pow(XtoMove, 2) );
            	}
            	
            	team2[i].set_velAngle(effVelAng.floatValue());
            	team2[i].set_velVal(effVelVal.floatValue() / sec); // distance on time
            	
            }	
            
            //Gestion de la balle ?
            	
            	
            //2nde étape, on gére le mouvement en lui même ou le shoot
            for(int i=0; i<teamSize; i++)
            {
            	///Team 1
            	
            	//On vérif s'il shoot oopa
            	//Shoot -> pas de mouvement, gestion (tps, etc)
            	if( team1[i].is_shooting() )
            	{}
            	//Mouvement normal
            	else
            	{
            		//lecture de la vitesse et de la position
            		float velVal = team1[i].get_velVal();
            		float velAng = team1[i].get_velAngle();
            		Position pos = team1[i].get_position();
            		
            		int newX = pos.getX() + (int)(Math.cos(velAng) * ( velVal * sec ));
            		int newY = pos.getY() + (int)(Math.sin(velAng) * ( velVal * sec ));
            		
            		//modif avec la nouvelle valeur
            		team1[i].setPosition(new Position(newX, newY) );
            	}
            	
            	///Team 2
            	
            	//Shoot -> pas de mouvement, gestion (tps, etc)
            	if( team2[i].is_shooting() )
            	{}
            	//Mouvement normal
            	else
            	{
            		//lecture de la vitesse et de la position
            		float velVal = team2[i].get_velVal();
            		float velAng = team2[i].get_velAngle();
            		Position pos = team2[i].get_position();
            		
            		int newX = pos.getX() + (int)(Math.cos(velAng) * ( velVal * sec ));
            		int newY = pos.getY() + (int)(Math.sin(velAng) * ( velVal * sec ));
            		
            		//modif avec la nouvelle valeur
            		team2[i].setPosition(new Position(newX, newY) );
            	}
            }
            
            
            
            

            prevTime = D.getTime();
    }

    /**
     * @return the fieldWidth
     */
    public int getFieldWidth() 
    {
        return fieldWidth;
    }

    /**
     * @return the fieldHeight
     */
    public int getFieldHeight()
    {
        return fieldHeight;
    }

    /**
     * @return the teamSize
     */
    public int getTeamSize() 
    {
        return teamSize;
    }

    /**
     * @return the team1
     */
    public Player[] getTeam1() {
        return team1;
    }

    /**
     * @return the team2
     */
    public Player[] getTeam2() {
        return team2;
    }

    /**
     * @return the ball
     */
    public Ball getBall() {
        return ball;
    }

}
